#include "TcpConnection.h"
#include "TcpServer.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"

#include <functional>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

EventLoop* TcpConnection::CheckLoopNotNull(EventLoop* loop)
{
    if(loop==nullptr)
    {
        LOG_FATAL("%s:%s:%d TcpConnection Loop is null \n",__FILE__,__FUNCTION__,__LINE__);
    }
    return loop;
}

TcpConnection::TcpConnection(EventLoop* loop,const std::string& name,int sockfd,const InetAddress& localAddr,const InetAddress& peerAddr)
    : loop_(CheckLoopNotNull(loop))
    , name_(name)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop,sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
{
    // 给channel设置相应的回调函数 poller给channel通知感兴趣的事件发生了 channel会回调相应的函数
    channel_->setReadCallback(std::bind(&TcpConnection::handleRead,this,std::placeholders::_1));
    channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite,this));
    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose,this));
    channel_->setErrorCallback(std::bind(&TcpConnection::handleError,this));

    LOG_INFO("TcpConnection::ctor[%s] at fd=%d \n",name_.c_str(),sockfd);
    socket_->setKeepAlive(true);
}

TcpConnection::~TcpConnection()
{
    LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d \n",name_.c_str(),channel_->fd(),(int)state_);
}

void TcpConnection::send(const std::string& buf)
{
    if(state_==kConnected)
    {
        if(loop_->isInLoopThread())
        {
            sendInLoop(buf.c_str(),buf.size());
        }
        else
        {
            loop_->runInLoop(std::bind(&TcpConnection::sendInLoop,this,buf.c_str(),buf.size()));
        }
    }
}


void TcpConnection::sendInLoop(const void* data,size_t len)
{
    ssize_t nwrote=0;
    size_t remaining =len;
    bool faultError =false;

    // 之前调用过该connection的shutdown 不能再进行发送了
    if(state_==kDisconnected)
    {
        LOG_ERROR("disconnected give up writing \n");
    }

    // channel_第一次开始写数据 并且缓冲区没有待发送数据
    if(!channel_->isWriting()&& outputBuffer_.readableBytes()==0)
    {
        nwrote = ::write(channel_->fd(),data,len);
        if(nwrote>=0)
        {
            remaining=len-nwrote;
            if(remaining==0&&writeCompleteCallback_)
            {
                loop_->queueInLoop(std::bind(writeCompleteCallback_,shared_from_this()));

            }
        }
        else
        {
            nwrote=0;
            if(errno!=EWOULDBLOCK)
            {
                LOG_ERROR("TcpConnection::sendInLoop \n");
                if(errno==EPIPE||errno==ECONNRESET)
                {
                    faultError=true;
                }
            }
        }
    }

    // 这次write并没有把数据全部发送出去 剩余的数据需要保存到缓冲区中
    // 给channel注册epollout事件 poller发现tcp发送缓冲区有空间 会通知相应的channel调用handleWriteCallback
    // 也就是调用TcpConnection::handleWrite方法 把发送缓冲区的数据全部发送完成
    if(!faultError&&remaining>0)
    {
       outputBuffer_.append((char*)data+nwrote,remaining);
       if(!channel_->isWriting())
       {
        channel_->enableWriting();
       }
    }
}


void TcpConnection::shutdown()
{
    if(state_==kConnected)
    {
        setState(kDisconnecting);
        // 在连接所属的EventLoop线程中执行实际的关闭逻辑
        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop,this));
    }
}


void TcpConnection::shutdownInLoop()
{
    if(!channel_->isWriting()) // 当前outputBuffer中的数据已经全部发送完了
    {
        socket_->shutdownWrite();
    }
}

void TcpConnection::handleRead(Timestamp recvTime)
{
    int saveErrno=0;
    // Buffer类封装了readFd
    ssize_t n =inputBuffer_.readFd(channel_->fd(),&saveErrno);
    if(n>0)
    {
        messageCallback_(shared_from_this(),&inputBuffer_,recvTime);
    }
    else if(n==0)
    {
        handleClose();
    }
    else 
    {
        errno=saveErrno;
        LOG_ERROR("TcpConnection::handleRead:");
        handleError();
    }

}
void TcpConnection::handleWrite()
{
    if(channel_->isWriting())
    {
        int saveErrno = 0;
        // Buffer类封装了writeFd
        ssize_t n = outputBuffer_ .writeFd(channel_->fd(),&saveErrno);
        if(n>0)
        {
            outputBuffer_.retrieve(n);
            if(outputBuffer_.readableBytes()==0)
            {
                channel_->disableWriting();
                if(writeCompleteCallback_)
                {
                    // 唤醒loop_对应的thread线程 执行回调
                    loop_->queueInLoop(std::bind(writeCompleteCallback_,shared_from_this()));
                }
                if(state_==kDisconnecting)
                {
                    shutdownInLoop();
                }
            }
        }
        else
        {
            LOG_ERROR("TcpConnection::handleWrite \n");
        }
    }
    else
    {
        LOG_ERROR("TcpConnection fd=%d is doen no more writing \n",channel_->fd());

    }
}
void TcpConnection::handleClose()
{
    LOG_INFO("TcpConnection::handleClose fd=%d state=%d \n",channel_->fd(),(int)state_);
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr connPtr(shared_from_this()); // 返回一个 std::shared_ptr<TcpConnection> 类型的智能指针，指向当前对象(this)
    connectionCallback_(connPtr); // 执行连接关闭的回调
    closeCallback_(connPtr); // 关闭连接的回调


}
void TcpConnection::handleError()
{
    int saveErrno=0;
    int optval;
    socklen_t optlen=sizeof optval;
    if(::getsockopt(channel_->fd(),SOL_SOCKET,SO_ERROR,&optval,&optlen)<0)
    {
        saveErrno=errno;
    }
    else
    {
        saveErrno=optval;
    }
    LOG_ERROR("TcpConnection::handelError name:%s SO_ERROR:%d \n",name_.c_str(),saveErrno);

}

void TcpConnection::connectEstablished()
{
    setState(kConnected);
    channel_->tie(shared_from_this());
    channel_->enableReading();

    connectionCallback_(shared_from_this());
}


void TcpConnection::connectDestroyed()
{
    if(state_==kConnected)
    {
        setState(kDisconnected);
        channel_->disableAll(); // 把channnel所感兴趣的事件从poller移除掉
        connectionCallback_(shared_from_this());
    }
    channel_->remove(); // 把channel从poller删除掉
}